#if !PocketPC
using System;
using System.Security;
using System.Security.Permissions;
using System.ComponentModel;
using System.Collections;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;

namespace GeoFramework.IO.Usb
{
    /// <summary>
    /// A generic base class for physical device classes.
    /// </summary>
    [SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode=true)]
    public abstract class UsbDeviceClass : IDisposable
    {
        private IntPtr pDeviceInformationSet;
        private IntPtr pHwndParent = IntPtr.Zero;
        private Guid pClassGuid;
        private ArrayList pDevices = new ArrayList();

        protected UsbDeviceClass(string guid)
            : this(new Guid(guid))
        {}

        /// <summary>
        /// Creates a new instance using the specified USB device class.
        /// </summary>
        /// <param name="classGuid">A <strong>Guid</strong> indicating a specific kind of USB device.</param>
        /// <remarks>All USB devices on the system are grouped according to their type.  For example, keyboards, mice, and
        /// hard drives are each organized into three unique classes.  Each device class is indentified by a GUID value.</remarks>
        protected UsbDeviceClass(Guid classGuid)
        {
            pClassGuid = classGuid; 

            // Get the device information set for the device
            pDeviceInformationSet = NativeMethods.SetupDiGetClassDevs(ref pClassGuid, 0, pHwndParent, NativeMethods.DIGCF_DEVICEINTERFACE | NativeMethods.DIGCF_PRESENT);
            int error = Marshal.GetLastWin32Error();
            if (pDeviceInformationSet.ToInt32() == NativeMethods.INVALID_HANDLE_VALUE)
                throw new Win32Exception(error);
        }

        ~UsbDeviceClass()
        {
            Dispose();
        }

        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        [SuppressUnmanagedCodeSecurity()]
        public void Dispose()
        {
            // No need to finalize
            GC.SuppressFinalize(this);

            // Destroy any created device information list
            if (pDeviceInformationSet != IntPtr.Zero)
            {
                NativeMethods.SetupDiDestroyDeviceInfoList(pDeviceInformationSet);
                pDeviceInformationSet = IntPtr.Zero;
            }
        }

        /// <summary>
        /// Returns a Stream connected to the USB device.
        /// </summary>
        /// <returns></returns>
        public abstract UsbDevice CreateDevice(UsbDeviceClass deviceClass, string devicePath, int deviceInstance, int reserved, int index);


        /// <summary>
        /// Gets the device class's guid.
        /// </summary>
        public Guid ClassGuid
        {
            get
            {
                return pClassGuid;
            }
        }

        /// <summary>
        /// Gets the list of devices of this device class.
        /// </summary>
        public ArrayList Devices
        {
            get
            {
                if (pDevices.Count == 0)
                {
                    int index = 0;
                    while (true)
                    {
                        SP_DEVICE_INTERFACE_DATA interfaceData = new SP_DEVICE_INTERFACE_DATA();

                        if (!NativeMethods.SetupDiEnumDeviceInterfaces(pDeviceInformationSet, null, ref pClassGuid, index, interfaceData))
                        {
                            int error = Marshal.GetLastWin32Error();
                            if (error != NativeMethods.ERROR_NO_MORE_ITEMS)
                                throw new Win32Exception(error);
                            break;
                        }

                        SP_DEVINFO_DATA devData = new SP_DEVINFO_DATA();
                        int size = 0;
                        if (!NativeMethods.SetupDiGetDeviceInterfaceDetail(pDeviceInformationSet, interfaceData, IntPtr.Zero, 0, ref size, devData))
                        {
                            int error = Marshal.GetLastWin32Error();
                            if (error != NativeMethods.ERROR_INSUFFICIENT_BUFFER)
                                throw new Win32Exception(error);
                        }

                        IntPtr buffer = Marshal.AllocHGlobal(size);
                        SP_DEVICE_INTERFACE_DETAIL_DATA detailData = new SP_DEVICE_INTERFACE_DETAIL_DATA();
                        detailData.cbSize = Marshal.SizeOf(typeof(SP_DEVICE_INTERFACE_DETAIL_DATA));
                        Marshal.StructureToPtr(detailData, buffer, false);

                        if (!NativeMethods.SetupDiGetDeviceInterfaceDetail(pDeviceInformationSet, interfaceData, buffer, size, ref size, devData))
                        {
                            int ErrorCode = Marshal.GetLastWin32Error();
                            Marshal.FreeHGlobal(buffer);
                            throw new Win32Exception(ErrorCode);
                        }

                        IntPtr pDevicePath = (IntPtr)((int)buffer + Marshal.SizeOf(typeof(int)));
                        string devicePath = Marshal.PtrToStringAuto(pDevicePath);
                        Marshal.FreeHGlobal(buffer);

                        // COMMENTED OUT:
                        //UsbDevice device = ;
                        pDevices.Add(CreateDevice(this, devicePath, devData.devInst, devData.reserved, index));

                        index++;
                    }
                    pDevices.Sort();
                }
                return pDevices;
            }
        }
       
        [SuppressUnmanagedCodeSecurity()]
        internal string GetProperty(SP_DEVINFO_DATA devData, int property, string defaultValue)
        {
            if (devData == null)
                throw new ArgumentNullException("devData");

            int propertyRegDataType = 0;
            int requiredSize;
            int propertyBufferSize = 1024;

            IntPtr propertyBuffer = Marshal.AllocHGlobal(propertyBufferSize);
            if (!NativeMethods.SetupDiGetDeviceRegistryProperty(pDeviceInformationSet,
                devData,
                property,
                out propertyRegDataType,
                propertyBuffer,
                propertyBufferSize,
                out requiredSize))
            {
                int error = Marshal.GetLastWin32Error();
                Marshal.FreeHGlobal(propertyBuffer);
                if (error != NativeMethods.ERROR_INVALID_DATA)
                    throw new Win32Exception(error);
                return defaultValue;
            }

            string value = Marshal.PtrToStringAuto(propertyBuffer);
            Marshal.FreeHGlobal(propertyBuffer);
            return value;
        }

        [SuppressUnmanagedCodeSecurity()]
        internal int GetProperty(SP_DEVINFO_DATA devData, int property, int defaultValue)
        {
            if (devData == null)
                throw new ArgumentNullException("devData");

            int propertyRegDataType = 0;
            int requiredSize;
            int propertyBufferSize = Marshal.SizeOf(typeof(int));

            IntPtr propertyBuffer = Marshal.AllocHGlobal(propertyBufferSize);
            if (!NativeMethods.SetupDiGetDeviceRegistryProperty(pDeviceInformationSet,
                devData,
                property,
                out propertyRegDataType,
                propertyBuffer,
                propertyBufferSize,
                out requiredSize))
            {
                int error = Marshal.GetLastWin32Error();
                Marshal.FreeHGlobal(propertyBuffer);
                if (error != NativeMethods.ERROR_INVALID_DATA)
                    throw new Win32Exception(error);
                return defaultValue;
            }

            int value = (int)Marshal.PtrToStructure(propertyBuffer, typeof(int));
            Marshal.FreeHGlobal(propertyBuffer);
            return value;
        }

        //internal Guid GetProperty(SP_DEVINFO_DATA devData, int property, Guid defaultValue)
        //{
        //    if (devData == null)
        //        throw new ArgumentNullException("devData");

        //    int propertyRegDataType = 0;
        //    int requiredSize;
        //    int propertyBufferSize = Marshal.SizeOf(typeof(Guid));

        //    IntPtr propertyBuffer = Marshal.AllocHGlobal(propertyBufferSize);
        //    if (!NativeMethods.SetupDiGetDeviceRegistryProperty(pDeviceInformationSet,
        //        devData,
        //        property,
        //        out propertyRegDataType,
        //        propertyBuffer,
        //        propertyBufferSize,
        //        out requiredSize))
        //    {
        //        Marshal.FreeHGlobal(propertyBuffer);
        //        int error = Marshal.GetLastWin32Error();
        //        if (error != NativeMethods.ERROR_INVALID_DATA)
        //            throw new Win32Exception(error);
        //        return defaultValue;
        //    }

        //    Guid value = (Guid)Marshal.PtrToStructure(propertyBuffer, typeof(Guid));
        //    Marshal.FreeHGlobal(propertyBuffer);
        //    return value;
        //}

    }
}
#endif